package org.dizem.intellipainter.component;

import org.dizem.intellipainter.AppConstants;
import org.dizem.intellipainter.MainFrame;
import org.dizem.intellipainter.model.Shape;
import org.dizem.intellipainter.model.ShapeType;
import org.dizem.intellipainter.model.ToolType;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.LinkedList;
import java.util.List;

/**
 * User: dizem@126.com
 * Time: 12-2-18 下午7:23
 */
public class PaintBoard extends JPanel implements MouseListener, MouseMotionListener {
	private final MainFrame frame;
	private final List<Shape> shapeList = new LinkedList<Shape>();

	private enum State {
		READY, DRAG
	}

	private State state = State.READY;
	private Point startPoint;
	private Point endPoint;

	public PaintBoard(MainFrame frame) {
		super(true);
		this.frame = frame;
		setBackground(Color.WHITE);
		setBorder(BorderFactory.createLineBorder(new Color(0xD6D9DF), 2));
		addMouseListener(this);
		addMouseMotionListener(this);
	}


	public void mouseClicked(MouseEvent e) {
	}

	public void mousePressed(MouseEvent e) {
		if (state == State.READY) {
			state = State.DRAG;
			startPoint = new Point(e.getX(), e.getY());
		}
	}


	public void mouseReleased(MouseEvent e) {
		if (state == State.DRAG) {
			state = State.READY;
			switch (frame.getToolType()) {
				case Pen:
					shapeList.add(new Shape(frame.getShapeType(), frame.getStrokeType(), startPoint, endPoint, frame.getColor1(), frame.getColor2()));
					break;
				case Brush:
					break;
				case Fill:
					break;
				case Text:
					String text = JOptionPane.showInputDialog(frame, "输入文字");
					if (text != null && !text.isEmpty()) {
						shapeList.add(new Shape(text, frame.getColor1(), startPoint));
						repaint();
					}
					break;
				case Eraser:
					ShapeType type = frame.getShapeType();
					ShapeType typeToEraser;
					if (type == ShapeType.Circle) {
						typeToEraser = ShapeType.FillCircle;
					} else if (type == ShapeType.Rect) {
						typeToEraser = ShapeType.FillRect;
					} else {
						typeToEraser = type;
					}
					shapeList.add(new Shape(typeToEraser, frame.getStrokeType(), startPoint, endPoint, Color.WHITE, Color.WHITE));
					repaint();
					break;
				case PickColor:
					break;
			}
		}
	}

	public void mouseEntered(MouseEvent e) {
		if (frame.getToolType() == ToolType.Text) {
			frame.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
		} else {
			frame.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
		}
	}

	public void mouseExited(MouseEvent e) {
		frame.setCursor(Cursor.getDefaultCursor());
		state = State.READY;
	}

	public void mouseDragged(MouseEvent e) {
		if (state == State.DRAG) {
			endPoint = new Point(e.getX(), e.getY());
			if (frame.getToolType() == ToolType.Pen && frame.getShapeType() == ShapeType.Point) {
				shapeList.add(new Shape(frame.getShapeType(), frame.getStrokeType(), startPoint, endPoint, frame.getColor1(), frame.getColor2()));
			} else if (frame.getToolType() == ToolType.Eraser && frame.getShapeType() == ShapeType.Point) {
				shapeList.add(new Shape(frame.getShapeType(), frame.getStrokeType(), startPoint, endPoint, Color.WHITE, Color.WHITE));
			}
			repaint();
		}
	}

	public void mouseMoved(MouseEvent e) {

	}

	@Override
	public void paint(Graphics g) {
		super.paint(g);
//		g.drawString(frame.getCodeBase().getPath() + "", 10, 10);
//		g.drawString(frame.getDocumentBase().getPath() + "", 10, 30);
		Graphics2D g2d = (Graphics2D) g;
		RenderingHints qualityHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		qualityHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
		g2d.setRenderingHints(qualityHints);
		for (Shape shape : shapeList) {
			shape.draw(g);
		}

		switch (frame.getToolType()) {
			case Pen:
				drawWithPen(g);
				break;
			case Brush:
				break;
			case Fill:
				break;
			case Text:
				break;
			case Eraser:
				drawWithEraser(g);

				break;
			case PickColor:
				break;
		}


		if (showAnimation) {
			g.setColor(frame.getColor1());
			g.drawImage(imageList.get(animationIndex), 350, 100, null);
			g.drawString(AppConstants.MARQUEE_TEXT, 300 + marqueePos, 100);
		}
	}

	private void drawWithEraser(Graphics g) {
		g.setColor(Color.GREEN);
		((Graphics2D) g).setStroke(frame.getStrokeType().getStroke());
		switch (frame.getShapeType()) {
			case Point:
				if (state == State.DRAG) {
					int width = (int) frame.getStrokeType().getStroke().getLineWidth();
					g.drawOval((endPoint.x - width) >> 1, (endPoint.y - width) >> 1, width, width);
				}
				break;
			case Rect:
				if (state == State.DRAG) {
					int leftTopX = Math.min(startPoint.x, endPoint.x);
					int leftTopY = Math.min(startPoint.y, endPoint.y);
					int width = Math.abs(endPoint.x - startPoint.x);
					int height = Math.abs(endPoint.y - startPoint.y);
					g.drawRect(leftTopX, leftTopY, width, height);
				}
				break;
			case Circle:
				if (state == State.DRAG) {
					int leftTopX = Math.min(startPoint.x, endPoint.x);
					int leftTopY = Math.min(startPoint.y, endPoint.y);
					int width = Math.abs(endPoint.x - startPoint.x);
					int height = Math.abs(endPoint.y - startPoint.y);
					g.drawOval(leftTopX, leftTopY, width, height);
				}
				break;
			case Line:
				if (state == State.DRAG) {
					g.drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y);
				}
				break;
			case FillRect:
				if (state == State.DRAG) {
					int leftTopX = Math.min(startPoint.x, endPoint.x);
					int leftTopY = Math.min(startPoint.y, endPoint.y);
					int width = Math.abs(endPoint.x - startPoint.x);
					int height = Math.abs(endPoint.y - startPoint.y);
					g.drawRect(leftTopX, leftTopY, width, height);
				}
				break;
			case FillCircle:
				if (state == State.DRAG) {
					int leftTopX = Math.min(startPoint.x, endPoint.x);
					int leftTopY = Math.min(startPoint.y, endPoint.y);
					int width = Math.abs(endPoint.x - startPoint.x);
					int height = Math.abs(endPoint.y - startPoint.y);
					g.drawOval(leftTopX, leftTopY, width, height);
				}
				break;
		}
	}

	private void drawWithPen(Graphics g) {
		g.setColor(frame.getColor1());
		((Graphics2D) g).setStroke(frame.getStrokeType().getStroke());
		switch (frame.getShapeType()) {
			case Point:
				if (state == State.DRAG) {
					int width = (int) frame.getStrokeType().getStroke().getLineWidth();
					g.fillOval(endPoint.x - width / 2, endPoint.y - width / 2, width, width);
				}
				break;
			case Rect:
				if (state == State.DRAG) {
					int leftTopX = Math.min(startPoint.x, endPoint.x);
					int leftTopY = Math.min(startPoint.y, endPoint.y);
					int width = Math.abs(endPoint.x - startPoint.x);
					int height = Math.abs(endPoint.y - startPoint.y);
					g.drawRect(leftTopX, leftTopY, width, height);
				}
				break;
			case Circle:
				if (state == State.DRAG) {
					int leftTopX = Math.min(startPoint.x, endPoint.x);
					int leftTopY = Math.min(startPoint.y, endPoint.y);
					int width = Math.abs(endPoint.x - startPoint.x);
					int height = Math.abs(endPoint.y - startPoint.y);
					g.drawOval(leftTopX, leftTopY, width, height);
				}
				break;
			case Line:
				if (state == State.DRAG) {
					g.drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y);
				}
				break;
			case FillRect:
				if (state == State.DRAG) {
					int leftTopX = Math.min(startPoint.x, endPoint.x);
					int leftTopY = Math.min(startPoint.y, endPoint.y);
					int width = Math.abs(endPoint.x - startPoint.x);
					int height = Math.abs(endPoint.y - startPoint.y);
					g.setColor(frame.getColor2());
					g.fillRect(leftTopX + 1, leftTopY + 1, width - 1, height - 1);
					g.setColor(frame.getColor1());
					g.drawRect(leftTopX, leftTopY, width, height);
				}
				break;
			case FillCircle:
				if (state == State.DRAG) {
					int leftTopX = Math.min(startPoint.x, endPoint.x);
					int leftTopY = Math.min(startPoint.y, endPoint.y);
					int width = Math.abs(endPoint.x - startPoint.x);
					int height = Math.abs(endPoint.y - startPoint.y);
					g.setColor(frame.getColor2());
					g.fillOval(leftTopX + 1, leftTopY + 1, width - 1, height - 1);
					g.setColor(frame.getColor1());
					g.drawOval(leftTopX, leftTopY, width, height);
				}
				break;
		}
	}

	/**
	 * Clear the board
	 */
	public void clear() {
		shapeList.clear();
		repaint();
		state = State.READY;
	}


	/**
	 * Undo the last operation
	 *
	 * @return if undo success then return true
	 */
	public void undo() {
		if (shapeList.size() > 0) {
			shapeList.remove(shapeList.size() - 1);
			repaint();
			state = State.READY;
			return;
		} else {
			return;
		}
	}

	private boolean showAnimation = false;
	private List<Image> imageList = null;
	private int animationIndex = 0;
	private int marqueePos = 0;
	private int marqueeDir = 1;
	private final Thread animationThread = new Thread(new Runnable() {
		public void run() {
			while (showAnimation) {
				try {
					Thread.sleep(100);
					animationIndex = (animationIndex + 1) % imageList.size();
					marqueePos += marqueeDir * 5;
					if (marqueePos > 150 || marqueePos < 0) {
						marqueeDir *= -1;
					}
					repaint();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	});

	public void showAnimation(List<Image> imageList) {
		if (imageList == null || imageList.size() == 0) {
			return;
		}
		showAnimation = true;
		this.imageList = imageList;
		if (!animationThread.isAlive()) {
			animationThread.start();
		} else {
			animationThread.resume();
		}
	}

	public void hideAnimation() {
		showAnimation = false;
		animationThread.suspend();
		repaint();
	}
}
